package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"snail/util"
	"sort"
	"strings"
	"time"
)

// 注册的命令
var cmds = map[string]string{
	"add":    "slice",
	"get":    "string",
	"del":    "string",
	"finish": "string",
	"begin":  "string",
	"info":   "string",
	"log":    "string",
	"modify": "string",
	"mv":     "slice",
	"help":   "string",
}

func main() {
	// 检查命令是否有效
	cmd, data, err := checkCmd()
	if err != nil {
		util.Print(err.Error())
		return
	}

	initFile() // 初始化文件

	// 获取文件中的数据
	snailData := initData()

	// 执行对应操作
	switch cmd {
	case "add":
		err = add(data.([]string), snailData)
	case "get":
		err = get(data.(string), snailData)
	case "del":
		err = delete(data.(string), snailData)
	case "modify":
		err = modify(data.(string), snailData)
	case "finish":
		err = finish(data.(string), snailData)
	case "mv":
		err = move(data.([]string), snailData)
	case "log":
		err = getLog(data.(string))
	case "begin":
		err = begin(data.(string), snailData)
	case "info":
		err = info(data.(string), snailData)
	case "help":
		err = help()

	}
	if err != nil {
		util.Print(err)
	}
}

func add(val []string, snailData util.SnailData) error {
	//notice := "value date[1m/1d/1h/2002-05-22 11:11:11/2002-05-22 11:11/2002-05-22 11/2002-05-22/20020522]"
	var lastTime int64 = 0
	var typeStr string

	if len(val) == 0 {
		return util.Err{"错误的输入"}
	}
	if len(val) == 1 { // 只有添加内容的情况
		typeStr = util.WaitInput("请输入任务分级 A重要紧急 B重要不紧急 C不重要紧急 D不重要不紧急")
		typeStr = strings.ToLower(typeStr)
		timeStr := util.WaitInputEnter("请输入截止时间 (1m/1d/1h/2002-05-22 11:11:11/2002-05-22 11:11/2002-05-22 11/2002-05-22/20020522)")
		if timeStr != "" {
			var err error
			lastTime, err = util.TimeToUnix(timeStr)
			if err != nil {
				return err
			}
		}
	} else if len(val) == 2 { // 有添加内容和类型
		typeStr = strings.ToLower(val[1])
		timeStr := util.WaitInputEnter("请输入截止时间 (1m/1d/1h/2002-05-22 11:11:11/2002-05-22 11:11/2002-05-22 11/2002-05-22/20020522)")
		if timeStr != "" {
			var err error
			lastTime, err = util.TimeToUnix(timeStr)
			if err != nil {
				return err
			}
		}
	} else if len(val) > 2 { // 有添加内容和类型和截止时间
		typeStr = strings.ToLower(val[1])
		var err error
		lastTime, err = util.TimeToUnix(strings.Join(val[2:], " "))
		if err != nil {
			return err
		}
	}

	if !util.IsInStrSlice(util.TypeSlice, typeStr) {
		return util.Err{"错误的类型输入"}
	}
	var typeData = util.GetTypeData(val[0], lastTime)
	snailData = addByType(typeStr, snailData, typeData)
	util.Log(util.GetLogStruct(util.LogTypeAdd, typeData.Val, typeData))
	return flushFile(snailData)
}

func get(val string, snailData util.SnailData) error {
	val = strings.ToLower(val)
	if val == "" || val == "all" {
		printTypeData(util.TypeA, util.MsgA, snailData.TypeA)
		printTypeData(util.TypeB, util.MsgB, snailData.TypeB)
		printTypeData(util.TypeC, util.MsgC, snailData.TypeC)
		printTypeData(util.TypeD, util.MsgD, snailData.TypeD)
	} else if val == "detail" {
		printTypeDataDetail(util.TypeA, util.MsgA, snailData.TypeA)
		printTypeDataDetail(util.TypeB, util.MsgB, snailData.TypeB)
		printTypeDataDetail(util.TypeC, util.MsgC, snailData.TypeC)
		printTypeDataDetail(util.TypeD, util.MsgD, snailData.TypeD)

	} else if util.IsInStrSlice(util.TypeSlice, val) {
		switch strings.ToLower(val) {
		case "a":
			printTypeData(util.TypeA, util.MsgA, snailData.TypeA)
		case "b":
			printTypeData(util.TypeB, util.MsgB, snailData.TypeB)
		case "c":
			printTypeData(util.TypeC, util.MsgC, snailData.TypeC)
		case "d":
			printTypeData(util.TypeD, util.MsgD, snailData.TypeD)
		}
	} else if val == "one" { // 推荐当前应该处理的任务
		sortDataList := getSortData(snailData)
		printData(sortDataList[0].DataType, fmt.Sprintf("%s%d:", sortDataList[0].DataType, sortDataList[0].Num), []util.TypeData{sortDataList[0].Data})
	} else if val == "top" {
		sortDataList := getSortData(snailData)
		if len(sortDataList) > 0 {
			printData(sortDataList[0].DataType, fmt.Sprintf("%s%d:", sortDataList[0].DataType, sortDataList[0].Num), []util.TypeData{sortDataList[0].Data})
		}
		if len(sortDataList) > 1 {
			printData(sortDataList[1].DataType, fmt.Sprintf("%s%d:", sortDataList[1].DataType, sortDataList[1].Num), []util.TypeData{sortDataList[1].Data})
		}
		if len(sortDataList) > 2 {
			printData(sortDataList[2].DataType, fmt.Sprintf("%s%d:", sortDataList[2].DataType, sortDataList[2].Num), []util.TypeData{sortDataList[2].Data})
		}
	}
	return nil
}

func delete(val string, snailData util.SnailData) error {
	val = strings.ToLower(val)
	if val == "" {
		return util.Err{"请输入要删除的项"}
	}
	isSure := util.WaitInput("确定删除(Y/n)")
	if strings.ToLower(isSure) != "y" {
		return util.Err{"已取消操作"}
	}

	compileRegex := regexp.MustCompile("^([a,b,c,d])([0-9]+)$")
	matchArr := compileRegex.FindStringSubmatch(val)
	if len(matchArr) == 3 {
		typeStr := matchArr[1]
		lineNum := util.StrToInt(matchArr[2])
		typeData := getSnailDataTypeOne(snailData, typeStr, lineNum)            // 获取数据
		snailData = deleteSnailData(snailData, typeStr, lineNum)                // 删除数据
		util.Log(util.GetLogStruct(util.LogTypeDelete, typeData.Val, typeData)) // 增加删除日志
		return flushFile(snailData)
	}

	if val == "all" {
		data, _ := json.Marshal(snailData)
		snailData = util.SnailData{}
		util.Log(util.GetLogStruct(util.LogTypeDelete, "清除所有任务:"+string(data), util.TypeData{}))
	} else if util.IsInStrSlice(util.TypeSlice, val) {
		var data []byte
		switch strings.ToLower(val) {
		case util.TypeA:
			data, _ = json.Marshal(snailData.TypeA)
			snailData.TypeA = []util.TypeData{}
		case util.TypeB:
			data, _ = json.Marshal(snailData.TypeB)
			snailData.TypeB = []util.TypeData{}
		case util.TypeC:
			data, _ = json.Marshal(snailData.TypeC)
			snailData.TypeC = []util.TypeData{}
		case util.TypeD:
			data, _ = json.Marshal(snailData.TypeD)
			snailData.TypeD = []util.TypeData{}
		}
		util.Log(util.GetLogStruct(util.LogTypeDelete, "清除所有任务"+strings.ToLower(val)+":"+string(data), util.TypeData{}))
	} else {
		return util.Err{"请输入正确的格式 a b c d a1 all"}
	}

	return flushFile(snailData)

}

func modify(val string, snailData util.SnailData) error {
	val = strings.ToLower(val)
	if val == "" {
		return util.Err{"请输入要修改的项 如a1"}
	}
	compileRegex := regexp.MustCompile("^([a,b,c,d])([0-9]+)$")
	matchArr := compileRegex.FindStringSubmatch(val)
	if len(matchArr) == 3 {
		typeStr := matchArr[1]
		lineNum := util.StrToInt(matchArr[2])
		logData := util.GetLogStruct(util.LogTypeModify, "", util.TypeData{})
		var err error = nil
		switch typeStr {
		case util.TypeA:
			snailData.TypeA, err = handleModify(snailData.TypeA, lineNum, &logData)
		case util.TypeB:
			snailData.TypeB, err = handleModify(snailData.TypeB, lineNum, &logData)
		case util.TypeC:
			snailData.TypeC, err = handleModify(snailData.TypeC, lineNum, &logData)
		case util.TypeD:
			snailData.TypeD, err = handleModify(snailData.TypeD, lineNum, &logData)
		}
		if err != nil {
			return err
		}

		util.Log(logData)
		return flushFile(snailData)
	} else {
		return util.Err{"请输入正确的格式"}
	}

	return nil
}

func finish(val string, snailData util.SnailData) error {
	// 获取移动前后的类型以及行数
	compileRegex := regexp.MustCompile("^([a,b,c,d])([0-9]+)$")
	matchArr := compileRegex.FindStringSubmatch(val)
	if len(matchArr) != 3 {
		return util.Err{"请输入正确的参数如mv a1 d"}
	}
	typeStr := matchArr[1]
	lineNum := util.StrToInt(matchArr[2])

	typeData := getSnailDataTypeOne(snailData, typeStr, lineNum) // 获取数据
	if typeData.Val == "" {
		return util.Err{"没有找到要完成的任务"}
	}
	snailData = deleteSnailData(snailData, typeStr, lineNum)                // 删除数据
	util.Log(util.GetLogStruct(util.LogTypeFinish, typeData.Val, typeData)) // 增加删除日志
	return flushFile(snailData)
}

func move(val []string, snailData util.SnailData) error {
	if len(val) != 2 {
		return util.Err{"请输入正确的参数如mv a1 d"}
	}

	// 获取移动前后的类型以及行数
	compileRegex := regexp.MustCompile("^([a,b,c,d])([0-9]+)$")
	matchArr := compileRegex.FindStringSubmatch(val[0])
	if len(matchArr) != 3 {
		return util.Err{"请输入正确的参数如mv a1 d"}
	}
	typeStr := matchArr[1]
	lineNum := util.StrToInt(matchArr[2])

	// 获取要移动的内容
	typeData := getSnailDataTypeOne(snailData, typeStr, lineNum)
	if typeData.Val == "" {
		return util.Err{"未获取到要移动的内容"}
	}

	// 添加内容
	if !util.IsInStrSlice(util.TypeSlice, strings.ToLower(val[1])) {
		return util.Err{"请输入正确的类型 a,b,c,d"}
	}
	snailData = addByType(strings.ToLower(val[1]), snailData, typeData)

	// 删除原先的
	snailData = deleteSnailData(snailData, typeStr, lineNum)

	// 写日志
	var logData util.LogStruct
	logData.Type = util.LogTypeMove
	logData.UniqId = typeData.UniqId
	logData.TypeData = typeData
	data, _ := json.Marshal(typeData)
	logData.Content = fmt.Sprintf("%s -> %s typeData:%s", typeStr, strings.ToLower(val[1]), string(data))
	util.Log(logData)

	// 刷新
	return flushFile(snailData)
}

func getLog(val string) error {
	var maxLine int
	if val == "" {
		maxLine = 10
		util.GetLog(maxLine)
	} else if val == "more" {
		maxLine = 100
		util.GetLog(maxLine)
	} else if util.StrToInt64(val) != 0 {
		util.GetUniqIdLog(util.StrToInt64(val))
	} else if util.IsInStrSlice(util.LogTypeSlice, strings.ToLower(val)) {
		util.GetTypeLog(strings.ToLower(val))
	} else {
		return util.Err{"错误的输入"}
	}
	return nil
}

func begin(val string, snailData util.SnailData) error {
	val = strings.ToLower(val)
	if val == "" {
		return util.Err{"请输入要开始的项 如a1"}
	}
	compileRegex := regexp.MustCompile("^([a,b,c,d])([0-9]+)$")
	matchArr := compileRegex.FindStringSubmatch(val)
	if len(matchArr) == 3 {
		typeStr := matchArr[1]
		lineNum := util.StrToInt(matchArr[2])
		var dataType util.TypeData
		switch typeStr {
		case util.TypeA:
			dataType = getTypeDataOne(lineNum, snailData.TypeA)
			snailData.TypeA = gridTypeData(lineNum, snailData.TypeA)
		case util.TypeB:
			dataType = getTypeDataOne(lineNum, snailData.TypeB)
			snailData.TypeB = gridTypeData(lineNum, snailData.TypeB)
		case util.TypeC:
			dataType = getTypeDataOne(lineNum, snailData.TypeC)
			snailData.TypeC = gridTypeData(lineNum, snailData.TypeC)
		case util.TypeD:
			dataType = getTypeDataOne(lineNum, snailData.TypeD)
			snailData.TypeD = gridTypeData(lineNum, snailData.TypeD)
		}
		logData := util.GetLogStruct(util.LogTypeBegin, dataType.Val, dataType)
		util.Log(logData)
	}
	return flushFile(snailData)
}

func info(val string, snailData util.SnailData) error {
	val = strings.ToLower(val)
	if val == "" {
		return util.Err{"请输入要开始的项 如a1"}
	}
	compileRegex := regexp.MustCompile("^([a,b,c,d])([0-9]+)$")
	matchArr := compileRegex.FindStringSubmatch(val)
	if len(matchArr) == 3 {
		typeStr := matchArr[1]
		lineNum := util.StrToInt(matchArr[2])
		var dataType util.TypeData
		switch typeStr {
		case util.TypeA:
			dataType = getTypeDataOne(lineNum, snailData.TypeA)
			snailData.TypeA = gridTypeData(lineNum, snailData.TypeA)
		case util.TypeB:
			dataType = getTypeDataOne(lineNum, snailData.TypeB)
			snailData.TypeB = gridTypeData(lineNum, snailData.TypeB)
		case util.TypeC:
			dataType = getTypeDataOne(lineNum, snailData.TypeC)
			snailData.TypeC = gridTypeData(lineNum, snailData.TypeC)
		case util.TypeD:
			dataType = getTypeDataOne(lineNum, snailData.TypeD)
			snailData.TypeD = gridTypeData(lineNum, snailData.TypeD)
		}
		util.Print("uniqId:", dataType.UniqId)
		util.Print("content:", dataType.Val)
		if dataType.Remark != "" {
			util.Print("remark:", dataType.Remark)
		}
		if dataType.LastTime != 0 {
			util.Print("lastTime:", dataType.LastTime)
		}
		if len(dataType.Grids) != 0 {
			util.Print("爬格子情况:")
			for k, v := range dataType.Grids {
				util.Print("->", k+1, util.FrontTime(v.Create))
			}
		}

	} else {
		return util.Err{"请检查数据是否正确"}
	}
	return nil
}

func help() error {
	str := " - snail add value [a|b|c|d] [截止时间:1d,1m,1h,具体时间]\n - snail get 获取所有列表\n - snail get a 获取对应分类下的任务列表\n - snail get one 智能推荐一个当前要处理的任务\n - snail get top 获取前三个需要处理的事情\n - snail get detail 详细输出所有列表内容\n - snail del all 清除所有\n - snail del a 清除对应分类下所有任务\n - snail del a1 清除对应分类下某条任务\n - snail modify a1 修改对应分类下某条任务\n - snail mv a1 b 将a分类中的第一个元素移动端b分类\n - snail log 获取最近的10条日志\n - snail log more 获取最近100条日志\n - snail log [uniqId] 获取对应任务id的所有日志\n - snail begin a1 开始执行某项任务\n - snail info a1 查询某项任务详情"
	fmt.Println(str)
	return nil
}

// common function
func checkCmd() (string, interface{}, error) {

	if len(os.Args) < 2 {
		return "", nil, util.Err{util.MsgHelp}
	}
	cmd := os.Args[1]
	if v, ok := cmds[cmd]; ok {
		if v == "slice" {
			if len(os.Args) == 2 {
				return cmd, nil, util.Err{util.MsgHelp}
			} else {
				return cmd, os.Args[2:], nil
			}
		} else if v == "string" {
			if len(os.Args) == 2 {
				return cmd, "", nil
			} else {
				return cmd, os.Args[2], nil
			}

		}
	}

	return "", nil, util.Err{util.MsgHelp}
}

func initFile() {
	// 拼装dataPath和logPath
	str, _ := filepath.Abs(filepath.Dir(os.Args[0]))
	if strings.Contains(str, "go-build") {
		util.DataPath = "./snail_data"
		util.LogPath = "./snail_log"
	} else {
		util.DataPath = str + "/snail_data"
		util.LogPath = str + "/snail_log"
	}
	if !util.CheckFileIsExist(util.DataPath) {
		_, err := os.Create(util.DataPath) //创建文件
		if err != nil {
			fmt.Printf("create data file err, %v\n", err)
			panic(err)
		}
	}
	if !util.CheckFileIsExist(util.LogPath) {
		_, err := os.Create(util.LogPath) //日志文件
		if err != nil {
			fmt.Printf("create log file err, %v\n", err)
			panic(err)
		}
	}
}

func initData() util.SnailData {
	content, err := ioutil.ReadFile(util.DataPath)
	if err != nil {
		fmt.Printf("read file err, %v\n", err)
	}

	var data util.SnailData
	_ = json.Unmarshal(content, &data)
	return data
}

func flushFile(snailData util.SnailData) error {
	content, _ := json.Marshal(snailData)
	err := ioutil.WriteFile(util.DataPath, content, 0777)
	return err
}

func addByType(typeStr string, snailData util.SnailData, typeData util.TypeData) util.SnailData {
	switch strings.ToLower(typeStr) {
	case util.TypeA:
		snailData.TypeA = append(snailData.TypeA, typeData)
	case util.TypeB:
		snailData.TypeB = append(snailData.TypeB, typeData)
	case util.TypeC:
		snailData.TypeC = append(snailData.TypeC, typeData)
	case util.TypeD:
		snailData.TypeD = append(snailData.TypeD, typeData)
	}
	return snailData
}

func getSortData(snailData util.SnailData) []util.SortData {
	var sortDataList []util.SortData
	for k, v := range snailData.TypeA {
		sortDataList = append(sortDataList, util.SortData{util.TypeA, k + 1, getWeight(util.TypeA, v), v})
	}
	for k, v := range snailData.TypeB {
		sortDataList = append(sortDataList, util.SortData{util.TypeB, k + 1, getWeight(util.TypeB, v), v})
	}
	for k, v := range snailData.TypeC {
		sortDataList = append(sortDataList, util.SortData{util.TypeC, k + 1, getWeight(util.TypeC, v), v})
	}
	for k, v := range snailData.TypeD {
		sortDataList = append(sortDataList, util.SortData{util.TypeD, k + 1, getWeight(util.TypeD, v), v})
	}
	sort.Slice(sortDataList, func(i, j int) bool {
		return sortDataList[i].Weight > sortDataList[j].Weight
	})
	return sortDataList
}

// 权重 a:4 b:3 c:2 d:1
// 截止时间 1天:4 2天:3 3天:2 四天及以上:1 无截止时间:0
// 创建时间 四天及以上:4 三天:3 两天:2 一天:1
func getWeight(t string, typeData util.TypeData) int {

	var weight int
	switch t {
	case util.TypeA:
		weight = 4
	case util.TypeB:
		weight = 3
	case util.TypeC:
		weight = 2
	case util.TypeD:
		weight = 1
	}

	// 截止时间
	nowUnix := time.Now().Unix()
	lastTime := typeData.LastTime
	if lastTime != 0 {
		a := lastTime - nowUnix
		if a < 60*60*24 {
			weight += 4
		} else if a < 60*60*24*2 {
			weight += 3
		} else if a < 60*60*24*3 {
			weight += 2
		} else {
			weight += 1
		}
	}

	// 创建时间
	createTime := typeData.CreateTime
	b := nowUnix - createTime
	if b < 60*60*24 {
		weight += 1
	} else if b < 60*60*24*2 {
		weight += 2
	} else if b < 60*60*24*3 {
		weight += 3
	} else {
		weight += 4
	}

	return weight

}

func printData(t, msg string, typeData []util.TypeData) {
	util.ColorPrint(util.TypeColorMap[t], msg)
	for _, v := range typeData {
		if v.LastTime == 0 {
			util.Print(v.Val)
		} else {
			util.Print(v.Val, util.FrontTime(v.LastTime))
		}
	}
}

func printTypeData(t, msg string, typeData []util.TypeData) {
	util.ColorPrintLn(util.TypeColorMap[t], msg)
	for k, v := range typeData {
		if v.LastTime == 0 {
			util.Print(k+1, v.Val)
		} else {
			util.Print(k+1, v.Val, util.FrontTime(v.LastTime))
		}
	}
	util.Print()
}

func printTypeDataDetail(t, msg string, typeData []util.TypeData) {
	util.ColorPrintLn(util.TypeColorMap[t], msg)
	for k, v := range typeData {
		if v.LastTime == 0 {
			util.Print(k+1, v.Val, "", util.FrontTime(v.CreateTime), v.UniqId)
		} else {
			util.Print(k+1, v.Val, util.FrontTime(v.LastTime), util.FrontTime(v.CreateTime), v.UniqId)
		}
	}
	util.Print()
}

func getSnailDataTypeOne(snailData util.SnailData, typeStr string, lineNum int) util.TypeData {
	var value util.TypeData
	switch typeStr {
	case util.TypeA:
		value = getTypeDataOne(lineNum, snailData.TypeA)
	case util.TypeB:
		value = getTypeDataOne(lineNum, snailData.TypeB)
	case util.TypeC:
		value = getTypeDataOne(lineNum, snailData.TypeC)
	case util.TypeD:
		value = getTypeDataOne(lineNum, snailData.TypeD)
	}
	return value
}

func getTypeDataOne(n int, typeData []util.TypeData) util.TypeData {
	var newTypeData = util.TypeData{}
	if len(typeData) >= n {
		for k, v := range typeData {
			if k+1 == n {
				newTypeData = v
			}
		}
	}
	return newTypeData
}

func gridTypeData(n int, typeData []util.TypeData) []util.TypeData {
	if len(typeData) >= n {
		for k, v := range typeData {
			if k+1 == n {
				v.Grids = append(v.Grids, util.Grid{util.SeTime()})
				typeData[k] = v
			}
		}
	}
	return typeData
}

func deleteSnailData(snailData util.SnailData, typeStr string, lineNum int) util.SnailData {
	switch typeStr {
	case util.TypeA:
		snailData.TypeA = deleteTypeDataSlice(lineNum, snailData.TypeA)
	case util.TypeB:
		snailData.TypeB = deleteTypeDataSlice(lineNum, snailData.TypeB)
	case util.TypeC:
		snailData.TypeC = deleteTypeDataSlice(lineNum, snailData.TypeC)
	case util.TypeD:
		snailData.TypeD = deleteTypeDataSlice(lineNum, snailData.TypeD)
	}
	return snailData
}

func deleteTypeDataSlice(n int, typeData []util.TypeData) []util.TypeData {
	if len(typeData) >= n {
		var newTypeData = make([]util.TypeData, 0)
		for k, v := range typeData {
			if k+1 != n {
				newTypeData = append(newTypeData, v)
			}
		}
		return newTypeData
	}
	return typeData
}

func handleModify(typeDataSlice []util.TypeData, lineNum int, logData *util.LogStruct) ([]util.TypeData, error) {
	if len(typeDataSlice) < lineNum {
		return typeDataSlice, util.Err{"超出范围"}
	}
	typeData := typeDataSlice[lineNum-1]
	logData.TypeData = typeData
	dataStr := util.WaitInputEnter(fmt.Sprintf("请输入修改后的内容: %s", typeData.Val))
	if dataStr != "" {
		typeData.Val = dataStr
	}

	// 添加备注
	isSure := util.WaitInputEnter("是否需要 添加/更新 备注(Y/n)")
	if strings.ToLower(isSure) == "y" {
		remark := util.WaitInputEndStr(typeData.Remark)
		if strings.TrimSpace(remark) != "" {
			typeData.Remark = remark
		}
	}

	var putTimeStr string
	if typeData.LastTime != 0 {
		putTimeStr = util.FrontTime(typeData.LastTime)
	}
	timeStr := util.WaitInputEnter(fmt.Sprintf("请输入修改后的截止时间: %s", putTimeStr))
	if timeStr != "" {
		var err error
		lastTime, err := util.TimeToUnix(timeStr)
		if err != nil {
			return typeDataSlice, err
		}
		typeData.LastTime = lastTime
	} else {
		typeData.LastTime = 0
	}
	typeDataSlice[lineNum-1] = typeData

	logData.Content = typeData.Val
	logData.UniqId = typeData.UniqId

	return typeDataSlice, nil
}
